using System;
using System.Net.Sockets;
using System.Net.Security;
using System.IO;
using System.Text;
using System.Security.Cryptography;
namespace fuzzyKittenVNC
{
	namespace VNCSecurity {
		public class VNCAuthException : Exception {};
		public class VNCInvalidPasswordException : Exception {};
		public class SecVNCAuthentification : VNCSecurity.PassSecImplementation
		{
			private string password = "Hi Guys!";
			public SecVNCAuthentification ()
			{
			}
			protected Socket socket = null;
			protected VNCClient.RFB_PROTOCOL_TYPE protoVersion;
			public int getHandlerNumber() {
				return 2;
			}
			public void setup(Socket socket,VNCClient.RFB_PROTOCOL_TYPE protoVersion) {			
				this.socket = socket;
				this.sendProtocolNr();
				this.getSecurityChallenge();
				this.getSecurityResult();
			}
			
			public void setPassword(string pass) {
				this.password = pass;
			}  
			protected byte[] createKey(string pass,int l) {
				byte[] key = new byte[l];
				byte s = 0;
				byte[] strByte = Encoding.ASCII.GetBytes(pass.PadRight(l,'\0'));
				for(int i=0;i<l;i++) {			
					key[i] = swapByte(strByte[i]);
				}
						
				return key;
			}
			
			/*
			 * thx to http://www.vidarholen.net/contents/junk/vnc.html
			 * 
			 */
			protected byte swapByte(byte inByte) {
				int b = (int) inByte;
				int r = 0;
				for(int x=0;x<4;x++) // lower nibble
					r = r | ((b&(128>>x))>>(7-x*2));
				for(int x=0,y=7;x<4;x++) // upper nibble
					r = r | ((b&(1<<x))<<(7-x*2));		
				return (byte) r;
			}
			
			public void getSecurityChallenge() {
				byte[] challenge = new byte[16];
				byte[] outBuffer = new byte[1024];
				Logger.log("Receiving sec challenge",Logger.LEVEL.DEBUG);
				if(this.receive(challenge,16) != 16)
					throw new VNCAuthException();
				Logger.log("Sec challenge received",Logger.LEVEL.DEBUG);

				outBuffer = this.encryptChallenge(challenge);
				
				this.send(outBuffer,outBuffer.Length);
			}
			
			protected byte[] encryptChallenge(byte[] challenge) {
				// DES Encryption
				DESCryptoServiceProvider des = new DESCryptoServiceProvider();
				
				try {					
					des.Key = this.createKey(this.password,des.KeySize/8);
				} catch(System.Security.Cryptography.CryptographicException e) {
					Logger.log(String.Format("{0}",e.Message),Logger.LEVEL.WARNING);	
				}
				des.Mode = CipherMode.ECB;
				//des.IV = des.Key;
				
				des.Padding = PaddingMode.Zeros;
				return des.CreateEncryptor().TransformFinalBlock(challenge,0,16);
			}
			
			public void sendProtocolNr() {
				byte[] nr = { Convert.ToByte(this.getHandlerNumber())};
				this.send(nr);
			}
			
			public void getSecurityResult() {
				byte[] securityResult = new byte[4];
				Logger.log("receiving sec result",Logger.LEVEL.DEBUG);
				this.receive(securityResult);
				Logger.log(String.Format("Security result received: {0}",securityResult),Logger.LEVEL.DEBUG);

				if(securityResult[3] == 1)
					throw new VNCInvalidPasswordException();
				if(!this.socket.Connected)
					throw new VNCSecurityTypeException();
			}
			
			public void send(byte[] msg) {
				if(!this.socket.Connected) 
					throw new VNCConnectionException();
				socket.Send(msg,msg.Length,SocketFlags.None);
			}
			
			public void send(byte[] msg,int l) {

				if(!this.socket.Connected) 
					throw new VNCConnectionException();
				socket.Send(msg,l,SocketFlags.None);
			}
			
			public int receive(byte[] msg,int c) {
				return socket.Receive(msg,c,SocketFlags.None);
			}
			
			public int receive(byte[] msg) {
				return socket.Receive(msg);
			}

		}
	}
}

